package com.yanfei.zero.application;

import java.time.LocalDateTime;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.yanfei.zero.domain.user.model.EventStore;

import cn.hutool.json.JSONUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
/**
 * 发布领域事件 保证数据库提交事务后
 * 到目前为止，我们对领域事件的处理都还没有涉及到与任何消息中间件相关的内容，也即事件的产生是一个完全独立于消息队列的关注点，
 * 此时我们不用关心领域事件之后将以何种形式发布出去，
 * Kafka也好，RabbitMQ也罢。除了关注点分离的好处外，这种解耦也使得系统在有可能切换消息中间件时更加的简单
 * 
 * 
 * 事件发布 1 事件作为通知机制 2事件携带状态转移
 * @author 123
 *
 */
@Aspect
@Component
public class EventPublishAspect {
	
	private static ExecutorService pool = Executors.newSingleThreadExecutor();
	private BlockingQueue<EventStore>tasks = new ArrayBlockingQueue<>(1024*1024);
	
	@Resource
	private RedisTemplate<String, Object> redisTemplate;
	
	@Resource(name="msDb")
    private DbService<EventStore> dbService;
	// Define pointcut for methods with @Encrypt annotation
	@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
	public void publishAnnotation() {}

	@Around("publishAnnotation()")
	public Object publishEvent(ProceedingJoinPoint joinPoint) throws Throwable {
		
        //获取方法签名(通过此签名获取目标方法信息)
        MethodSignature ms = (MethodSignature)joinPoint.getSignature();
        String interfaceName = ms.getName();
		// Get method arguments
		Object[] args = joinPoint.getArgs();
		
		// Proceed with method execution
		Object result = joinPoint.proceed(args);
		
		
		// 存储事件
		EventStore event = new EventStore();
		event.setEventStatus("CREATED");
		event.setEventType(interfaceName);
		event.setRaiseAt(LocalDateTime.now());
		event.setData(JSONUtil.toJsonPrettyStr(event));
		
		// 放入队列,也可以用redis替换
		tasks.add(event);
		

		
		return result;
		
	}
	
	
	@PostConstruct
	private void initData() {
		pool.submit( new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true) {
					try {
						// 获取队列中订单信息
						EventStore store=tasks.take();
						// 处理发布事件
						System.out.println(JSONUtil.toJsonPrettyStr(store));
						// 存入本地消息列表,解决消费失败重试
						dbService.saveData(store);
						redisTemplate.opsForHash().put(store.getId()+"",store,null);
						
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			
		});
	}

	
}
